
#include "memoryFile.h"
#include <sys/mman.h>

//MEMORY_FILE_MAP memoryFiles;
MEM_COLLECT memoryFiles;
int memoryFileCount = 0;
LOCK create_lock;
//PRINT lprint("log.out");

MEM_FILE_ITEM findMemoryFile(int id) {
	lprint.printDebug("function: findMemoryFile; id: %d \n", id);
	return memoryFiles.readMemoryFile(id);
}

int getFileNum(){
	lprint.printDebug("function: getFileNum \n");
	int mem_num = 0;
	create_lock.lock();
	mem_num = ++memoryFileCount;
	create_lock.unlock();
	return mem_num;
}

int createMemory(const char *filename, FILE_LEN_TYPE size) {
	lprint.printDebug("function: createMemory \n");
//	MEM_FILE *mem = new MEM_FILE();
	int mem_num = getFileNum();
	memoryFiles.push(mem_num);
	MEM_FILE_STAT* mem_stat = memoryFiles.get_stat(mem_num);
	MEM_FILE *mem = mem_stat->get_mem();
	mem->createMemory(filename, size);
	mem->position = tell(mem_num);
	mem->id = mem_num;
	lprint.printFile("create file id: %d; file name: %s \n", mem_num, mem->fileName.c_str());
	return mem_num;
}

void loadMemory(const char *filename, int& file_id, int& file_num){
	lprint.printDebug("function: loadMemory; file name: %s \n", filename);
	int mem_num = getFileNum();
	memoryFiles.push(mem_num);
	MEM_FILE_STAT* mem_stat = memoryFiles.get_stat(mem_num);
	MEM_FILE *mem = mem_stat->get_mem();
	LONG_INT file_size = mem->loadMemory(filename);
	FILE_LEN_TYPE index = HEAD_POS(file_size);
	HEAD_STRUCT *head = (HEAD_STRUCT*) mem->readPos(index);
	file_num = head->num;
	mem->id = mem_num;
	mem->fileName = filename;
	mem->fileSize = head->size;
	mem->closed = false;
	mem->position = head->position;
	file_id = mem_num;
}

void closeMemory(int id) {
	lprint.printDebug("function: closeMemory \n");
	MEM_FILE_ITEM mem = findMemoryFile(id);
	mem.quit();
//	delete mem;
	memoryFiles.erase(id);
}

void writeMemory(int id, char *dest, FILE_LEN_TYPE start, FILE_LEN_TYPE size) {
	lprint.printDebug("function: writeMemory \n");
	MEM_FILE_ITEM mem = findMemoryFile(id);
	mem.write(dest, start, size);
}

char* readMemory(int id, FILE_LEN_TYPE start, FILE_LEN_TYPE size) {
	lprint.printDebug("function: readMemory \n");
	MEM_FILE_ITEM mem = findMemoryFile(id);
	char *mem_char = mem.read(start, size);
	return mem_char;
}

int readByte(int id, FILE_LEN_TYPE start) {
	lprint.printDebug("function: readByte \n");
	MEM_FILE_ITEM mem = findMemoryFile(id);
	return mem.readByte(start);
}

void flushMemory(int id) {
	lprint.printDebug("function: flushMemory \n");
	MEM_FILE_ITEM mem = findMemoryFile(id);
	mem.flush();
}

int smallEndian() {
	lprint.printDebug("function: smallEndian \n");
	int i = 1;
	char *bg = (char*) &i;
	if (bg[0] == 1) {
		return 1;
	}
	return 0;
}

FILE_LEN_TYPE tell(int id) {
	lprint.printDebug("function: tell; id: %d \n", id);
	MEM_FILE_ITEM mem = findMemoryFile(id);
	return mem.tell();
}

void seek(int id, FILE_LEN_TYPE pos) {
	lprint.printDebug("function: seek \n");
	MEM_FILE_ITEM mem = findMemoryFile(id);
	mem.seek(pos);
}

FILE_LEN_TYPE appendMemory(int id, char *dest, FILE_LEN_TYPE size) {
	lprint.printDebug("function: appendMemory \n");
	MEM_FILE_ITEM mem = findMemoryFile(id);
	FILE_LEN_TYPE pos = mem.get_mem()->position;
	mem.write(dest, pos, size);
	mem.seek(pos + size);
	return pos;
}

FILE_LEN_TYPE appendMemory(int id, char *dest, FILE_LEN_TYPE size, FILE_RET_TYPE value) {
	lprint.printDebug("function: appendMemory \n");
	MEM_FILE_ITEM mem = findMemoryFile(id);
	FILE_LEN_TYPE pos = mem.get_mem()->position;
	mem.write(dest, pos, size);
	HEAD_STRUCT* head = mem.seek(pos + size);
	head->max = head->max > value ? head->max : value;
	head->min = head->min < value ? head->min : value;
	return pos;
}

void setFileInfo(int id, int file_num, FILE_LEN_TYPE size){
	MEM_FILE_ITEM mem = findMemoryFile(id);
	mem.setFileInfo(file_num, size);
}

void writeHead(int id, void* item){
	MEM_FILE_ITEM mem = findMemoryFile(id);
	mem.writeHead(item);
}

/***************************************************************************************************************************************************/

void memoryFile::load(){
	lprint.printDebug("function: memoryFile:load; id: %d file name: %s \n", memoryFile::id, memoryFile::fileName.c_str());
	if (closed){
		lprint.printFile("load file: %d; file name: %s \n", memoryFile::id, memoryFile::fileName.c_str());
		memoryFile::createMemory(memoryFile::fileName.c_str(), memoryFile::fileSize);
	}
}

void memoryFile::create(const char *filename, FILE_LEN_TYPE size) {
	lprint.printDebug("function: memoryFile:create \n");
	if (access(filename, F_OK) >= 0)
		return;

	FILE *fp;
	fp = fopen(filename, "ab+");
	if (fp) {
		FILE_LEN_TYPE all_size = FILE_ALL_SIZE(size);
		FILE_LEN_TYPE size_len = all_size / 1024 + all_size % 1024;
		char bytes[1024];
		memset(bytes, 0, sizeof(bytes));
		for (int i = 0; i < size_len; i++) {
			fwrite(bytes, sizeof(char), sizeof(bytes), fp);
		}
		fclose(fp);
	}
}

LONG_INT memoryFile::loadMemory(const char *filename){
	lprint.printDebug("function: memoryFile:loadMemory; file name: %s \n", filename);
	int fd;
	if ((fd = open(filename, O_RDWR)) < 0)
		lprint.printConsole("load memory open error: %s \n", filename);

	if ((fstat(fd, &fileStat)) == ERR_NUM)
		lprint.printConsole("load memory read state error: %s \n", filename);

	if ((memoryFile::mapped = (char*) mmap(NULL, fileStat.st_size, PROT_READ | PROT_WRITE,
			MAP_SHARED, fd, 0)) == (void*) ERR_NUM)
		lprint.printConsole("load memory read map error: %s \n", filename);

	LONG_INT file_size = fileStat.st_size;
	close(fd);
	lprint.printFile("load file size: %llu \n", file_size);
	return file_size;
}

char* memoryFile::createMemory(const char *filename, FILE_LEN_TYPE size) {
	lprint.printDebug("function: memoryFile:createMemory; file name: %s \n", filename);
	memoryFile::create(filename, size);
	LONG_INT file_size = memoryFile::loadMemory(filename);
	memoryFile::fileName = filename;
	memoryFile::fileSize = file_size;
	memoryFile::closed = false;
	return memoryFile::mapped;
}

void memoryFile::quit() {
	lprint.printDebug("function: memoryFile:quit \n");
	if ((munmap((void*) mapped, fileStat.st_size)) == ERR_NUM){
		perror("close memory file error");
		return;
	}
	closed = true;
}

HEAD_STRUCT* memoryFile::seek(FILE_LEN_TYPE pos){
	return seek(pos, memoryFile::readHead());
}

HEAD_STRUCT* memoryFile::seek(FILE_LEN_TYPE pos, HEAD_STRUCT* head){
	head_lock.lock();
	head->position = pos;
	memoryFile::position = pos;
	head_lock.unlock();
	return head;
}

FILE_LEN_TYPE memoryFile::tell(){
	head_lock.lock();
	HEAD_STRUCT *head = memoryFile::readHead();
	FILE_LEN_TYPE rtn_byte = head->position;
	head_lock.unlock();
	return rtn_byte;
}

void memoryFile::flush() {
	lprint.printDebug("function: memoryFile:flush \n");
	if ((msync((void*) mapped, fileStat.st_size, MS_SYNC)) == ERR_NUM)
		perror("sync error");
}

int memoryFile::readByte(FILE_LEN_TYPE start) {
	lprint.printDebug("function: memoryFile:readByte \n");
	return mapped[start];
}

void memoryFile::write(char *dest, FILE_LEN_TYPE start, FILE_LEN_TYPE size) {
	lprint.printDebug("function: memoryFile:write \n");
	memcpy(&mapped[start], dest, size);
}

char* memoryFile::read(FILE_LEN_TYPE start, FILE_LEN_TYPE size) {
	lprint.printDebug("function: memoryFile:read \n");
	char *dest = CHARS(size);
	memcpy(dest, &mapped[start], size);
	return dest;
}

char* memoryFile::readPos(FILE_LEN_TYPE start) {
	lprint.printDebug("function: memoryFile:readPos \n");
	return &mapped[start];
}

int memoryFile::compare(char *find, FILE_LEN_TYPE start, FILE_LEN_TYPE size) {
	lprint.printDebug("function: memoryFile:compare \n");
	for (int i = 0; i < size; i++) {
		if (find[i] != mapped[i + start])
			return 0;
	}
	return 1;
}

char* memoryFile::readMapper() {
	lprint.printDebug("function: memoryFile:readMapper \n");
	return mapped;
}

HEAD_STRUCT* memoryFile::readHead() {
	lprint.printDebug("function: memoryFile:readHead; file size: %llu \n", fileSize);
	FILE_LEN_TYPE index = HEAD_POS(fileSize);
	HEAD_STRUCT *head = (HEAD_STRUCT*) readPos(index);
	return head;
}

char* memoryFile::readHeadValue() {
	lprint.printDebug("function: memoryFile:readHeadValue \n");
	HEAD_STRUCT *head = readHead();
	char *head_value = CHARS(HEAD_LENGTH);
	memcpy(head, head_value, HEAD_LENGTH);
	return head_value;
}

void memoryFile::writeHead(FILE_LEN_TYPE pos, FILE_LEN_TYPE shift, FILE_LEN_TYPE max, FILE_LEN_TYPE min) {
	lprint.printDebug("function: memoryFile:writeHead \n");
	head_lock.lock();
	HEAD_STRUCT *head = readHead();
	head->position = pos;
	head->shift = shift;
	head_lock.unlock();
}

void memoryFile::writeHead(void *item) {
	lprint.printDebug("function: memoryFile:writeHead \n");
	head_lock.lock();
	HEAD_STRUCT *new_head = (HEAD_STRUCT*) item;
	HEAD_STRUCT *head = readHead();
	head->num = new_head->num;
	head->position = new_head->position;
	head->shift = new_head->shift;
	head->max = new_head->max;
	head->min = new_head->min;
	head_lock.unlock();
}

void memoryFile::setFileInfo(int file_num, FILE_LEN_TYPE size){
	HEAD_STRUCT *head = readHead();
	head->num = file_num;
	head->size = size;
}

void memoryFile::close_mem(){
	lprint.printDebug("function: memoryFile:close_mem \n");
	lock.lock();
	memoryFile::flush();
	memoryFile::quit();
	lock.unlock();
	lprint.printFile("memory file close: %d \n", memoryFile::id);
}

int memoryFile::find(char *context, char *search, FILE_LEN_TYPE context_size,
		FILE_LEN_TYPE search_size) {
	lprint.printDebug("function: memoryFile:find \n");
	FILE_LEN_TYPE context_p = 0, search_p = 0, shadow_p = 1, shadow_next_p = 0;
	while (true) {
		if (shadow_p + shadow_next_p >= context_size
				|| shadow_next_p >= search_size) {
			return ERR_NUM;
		} else if (context[shadow_p + shadow_next_p] == search[shadow_next_p]) {
			shadow_next_p += 1;
		} else {
			shadow_p += 1;
			shadow_next_p = 0;
		}

		if (context_p >= context_size || search_p >= search_size) {
			return context_p - search_p;
		} else if (context[context_p] == search[search_p]) {
			context_p += 1;
			search_p += 1;
		} else {
			context_p = shadow_p + shadow_next_p;
			search_p = shadow_next_p;
			shadow_p += 1;
			shadow_next_p = 0;
		}
	}
}

/***************************************************************************************************************************************************/

template <class obj> object<obj>::object(obj* obj_item){
	item = obj_item;
	objectBase* base = (objectBase*)item;
	base->create();
}

template <class obj> object<obj>::~object(){
	objectBase* base = (objectBase*)item;
	base->destroy();
}

template <class obj> obj* object<obj>::get(){
	return item;
}

/***************************************************************************************************************************************************/

template <class obj, class base> obj* valueObject<obj, base>::getValue(){
	return obj_item;
}

/***************************************************************************************************************************************************/

char* memoryCharItem::getChar(){
	return memoryCharItem::valueObject::getValue();
}

/***************************************************************************************************************************************************/

void memoryFileStat::destroy(){
	memoryFileStat::mem->lock.lock();
	run_stat_val = --reference <= 0 ? obj_stat::alive_stat : run_stat_val;
	memoryFileStat::mem->lock.unlock();
}

void memoryFileStat::create(){
	memoryFileStat::mem->lock.lock();
	if (obj_stat::run_stat == (run_stat_val & obj_stat::run_stat)){
		++reference = reference > 0 ? reference : 1;
	} else {
		memoryFileStat::get_mem()->load();
	}

	run_stat_val = obj_stat::run_stat & obj_stat::alive_stat;
	memoryFileStat::mem->lock.unlock();
}

bool memoryFileStat::alive(){
	return  obj_stat::alive_stat == (run_stat_val & obj_stat::alive_stat);
}

MEM_FILE* memoryFileStat::get_mem(){
	return mem;
}


void memoryFileStat::set_mem(MEM_FILE* mem_file){
	mem = mem_file;
}

/***************************************************************************************************************************************************/

void MEM_FILE_ITEM::create(const char *filename, FILE_LEN_TYPE size) {
	MEM_FILE_ITEM::get_mem()->create(filename, size);
}

LONG_INT MEM_FILE_ITEM::loadMemory(const char* file_name){
	return MEM_FILE_ITEM::get_mem()->loadMemory(file_name);
}

char* MEM_FILE_ITEM::createMemory(const char *filename, FILE_LEN_TYPE size) {
	return MEM_FILE_ITEM::get_mem()->createMemory(filename, size);
}

void MEM_FILE_ITEM::quit() {
	MEM_FILE_ITEM::get_mem()->quit();
}

void MEM_FILE_ITEM::flush() {
	MEM_FILE_ITEM::get_mem()->flush();
}

int MEM_FILE_ITEM::readByte(FILE_LEN_TYPE start) {
	return MEM_FILE_ITEM::get_mem()->readByte(start);
}

void MEM_FILE_ITEM::write(char *dest, FILE_LEN_TYPE start, FILE_LEN_TYPE size) {
	MEM_FILE_ITEM::get_mem()->write(dest, start, size);
}

char* MEM_FILE_ITEM::read(FILE_LEN_TYPE start, FILE_LEN_TYPE size) {
	return MEM_FILE_ITEM::get_mem()->read(start, size);
}

char* MEM_FILE_ITEM::readPos(FILE_LEN_TYPE start) {
	return MEM_FILE_ITEM::get_mem()->readPos(start);
}

int MEM_FILE_ITEM::compare(char *find, FILE_LEN_TYPE start, FILE_LEN_TYPE size) {
	return MEM_FILE_ITEM::get_mem()->compare(find, start, size);
}

CHAR_MEM MEM_FILE_ITEM::readMapper() {
	return MEM_FILE_ITEM::createChar(MEM_FILE_ITEM::get_mem()->readMapper());
}

HEAD_STRUCT* MEM_FILE_ITEM::readHead() {
	return MEM_FILE_ITEM::get_mem()->readHead();
}

char* MEM_FILE_ITEM::readHeadValue() {
	return MEM_FILE_ITEM::get_mem()->readHeadValue();
}

void MEM_FILE_ITEM::writeHead(FILE_LEN_TYPE pos, FILE_LEN_TYPE shift, FILE_LEN_TYPE max, FILE_LEN_TYPE min) {
	MEM_FILE_ITEM::get_mem()->writeHead(pos, shift, max, min);
}

HEAD_STRUCT* MEM_FILE_ITEM::seek(FILE_LEN_TYPE pos){
	return MEM_FILE_ITEM::get_mem()->seek(pos);
}

HEAD_STRUCT* MEM_FILE_ITEM::seek(FILE_LEN_TYPE pos, HEAD_STRUCT* head){
	return MEM_FILE_ITEM::get_mem()->seek(pos, head);
}

FILE_LEN_TYPE MEM_FILE_ITEM::tell(){
	return MEM_FILE_ITEM::get_mem()->tell();
}

void MEM_FILE_ITEM::writeHead(void *item) {
	MEM_FILE_ITEM::get_mem()->writeHead(item);
}

void MEM_FILE_ITEM::setFileInfo(int file_num, FILE_LEN_TYPE size){
	MEM_FILE_ITEM::get_mem()->setFileInfo(file_num, size);
}

int MEM_FILE_ITEM::find(char *context, char *search, FILE_LEN_TYPE context_size,
		FILE_LEN_TYPE search_size) {
	return MEM_FILE_ITEM::get_mem()->find(context, search, context_size, search_size);
}

MEM_FILE* MEM_FILE_ITEM::get_mem(){
	MEM_FILE_STAT* mem_stat = MEM_FILE_ITEM::object<MEM_FILE_STAT>::get();
	return mem_stat->get_mem();
}

CHAR_MEM MEM_FILE_ITEM::createChar(char* data){
	CHAR_MEM char_mem(data, MEM_FILE_ITEM::object::get());
	return char_mem;
}

FILE_LEN_TYPE MEM_FILE_ITEM::get_max(){
	return MEM_FILE_ITEM::get_mem()->max_value;
}

FILE_LEN_TYPE MEM_FILE_ITEM::get_min(){
	return MEM_FILE_ITEM::get_mem()->min_value;
}

/***************************************************************************************************************************************************/

memoryFileCollect::memoryFileCollect(){
	memoryFileCollect::out_cout = 1;
	THREAD trd(&memoryFileCollect::checkDestroy, this);
	trd.detach();
}

MEM_FILE_STAT* memoryFileCollect::get_stat(int id){
	return memoryFileCollect::get(id);
}

void memoryFileCollect::push(int id){
	MEM_FILE_STAT* mem_stat_val1 = new MEM_FILE_STAT();
	mem_map[id] = mem_stat_val1;
}

void memoryFileCollect::push(int id, MEM_FILE_STAT* st){
	mem_map[id] = st;
}

MEM_FILE_ITEM memoryFileCollect::readMemoryFile(int id){
	lprint.printDebug("function: memoryFileCollect::readMemoryFile; id: %d \n", id);
	lock.lock();
	MEM_FILE_STAT* mem_stat = memoryFileCollect::get(id);
	if (NULL == mem_stat){
		lprint.printFile("memory file not find: %d \n", id);
		MEM_FILE_ITEM item(NULL);
		return item;
	}
	MEM_FILE_ITEM val(mem_stat);
	lock.unlock();
	return val;
}

void memoryFileCollect::checkDestroy(){
	while (memoryFileCollect::run_stat){
		sleep(DESTROY_TIME);
		for (auto &mem_item : mem_map) {
			MEM_FILE_STAT* mem_stat = mem_item.second;
			if (mem_stat->alive()){
				mem_stat->run_stat_val = obj_stat::closeing_stat;
			} else if (mem_stat->run_stat_val == obj_stat::closeing_stat) {
				lock.lock();
				mem_stat->get_mem()->close_mem();
				mem_stat->run_stat_val = obj_stat::close_stat;
				lock.unlock();
			}
		}
	}
	memoryFileCollect::out_cout--;
}

MEM_FILE_STAT* memoryFileCollect::get(int id){
	if (memoryFileCollect::mem_map.count(id) > 0){
		return memoryFileCollect::mem_map[id];
	}
	lprint.printConsole("collect not find memory file: %d \n", id);
	return NULL;
}

bool memoryFileCollect::exist(int id){
	return memoryFileCollect::mem_map.count(id) > 0;
}

void memoryFileCollect::erase(int id){
	memoryFileCollect::mem_map.erase(id);
}

void memoryFileCollect::close(){
	memoryFileCollect::run_stat = false;
	while (out_cout>0){};
}

/***************************************************************************************************************************************************/


